/*
*********************************
	  SF2D Engine
*********************************

	By Julian Williams
	August 8, 2010
**********************************

	Description:

		Entity Factory class that
	controls all things within the 
	game's world.
***********************************
*/
#include <iostream>
#include <list>
#include <vector>
#include <utility>


#include "app.h"
#include "levels.h"
#include "helper.h"
#include "path_finder.h"
#include "physics_world.h"
#include "cfg_reader.h"

#include "entity_factory.h"
//individual entitys
#include "entity.h"
#include "creep.h"
#include "tower.h"
#include "projectile.h"


//single instance of an entity manager
Entity_Factory entityManager;

std::list < Entity* > Entity_Factory::entity_list;


//Register with Script engine!
bool Entity_Factory::Register_With_ScriptEngine(asIScriptEngine * engine)
{
	int r = 0;
	r = engine->RegisterObjectType("Entity_Factory", 0, asOBJ_REF | asOBJ_NOHANDLE);		assert( r >= 0 );
	
	r = engine->RegisterObjectMethod("Entity_Factory", "void Spawn(const string&, vector2&)", asMETHODPR(Entity_Factory, Spawn, (const string&, b2Vec2&), void), asCALL_THISCALL);		assert( r >= 0 );

	//Register main property
	r = engine->RegisterGlobalProperty("Entity_Factory entityManager", &entityManager); assert( r >= 0 );




	//needed reference to the entity list for towers
	Tower::entity_list_reference = &entity_list;



	if(r < 0) { return false; }
	return true;
}//EoF




//Spawn an entity at a location
void Entity_Factory::Spawn(const string& name, b2Vec2& start)
{
		EntityEmmiter emmiter;

		emmiter.name = name;
		emmiter.rotation = 0;
		emmiter.start_pos = start;
		emmiter.velocity.SetZero();

		Spawn(emmiter);
}//EoF


void Entity_Factory::Spawn( const EntityEmmiter& emmiter )
{
		
	map<string, Entity*>::iterator pos = Type.find(emmiter.name);
	if(pos != Type.end())
	{
			Entity * e;

			switch( pos->second->type )
			{
			case ET_CREEP:
				
				e = new Creep(*pos->second);
				cout << "Creep - " << pos->second->type << endl;
				break;

			case ET_TOWER:
				
				e = new Tower(*pos->second);
				
				cout << "Tower - " << pos->second->type << endl;
				break;

			case ET_PROJECTILE:


				e = new Projectile(*pos->second);

				cout << "Projectile - " << pos->second->type << endl;

				break;

			default:

				e = new Entity(*pos->second);
				
				cout << "Entity - " << pos->second->type << endl;
			//	return;
				break;
			}//EoS

			//Entity type prepares to spawn
			e->PrepareSpawn(emmiter.start_pos);
			
			//e->body->ApplyLinearImpulse(emmiter.velocity, e->body->GetWorldPoint( b2Vec2() ));
			e->body->ApplyLinearImpulse(emmiter.velocity, e->body->GetPosition());

			e->body->SetTransform( e->body->GetPosition(), degreesToRadians(emmiter.rotation) );

			entity_list.push_back( e );
			//sort the list so that the entities are all next to each others type, 
			//ie. creeps next creeps, towers next to towers, etc, fixes Render z-orders
			//and type searching as well! 

			entity_list.sort( Entity_ptr_cmp() );


	}else{
			cout << "[SPAWN] - Entity type: " << emmiter.name << " was not found." << endl;

	}//end ov finding entity to spawn
			cout << entity_list.size() << endl << endl;
}//EoF




//Load Types into the List (needed since we inheret from factory)
bool Entity_Factory::Load_TypeIntoList(const string& cfg_location)
{

		//make a particle Get Configurations
			string name_loc = cfg_location + string("properties.cfg");
			cfg_reader get_type(name_loc, true);

			//Get what kind of type it is
			int type = get_type.Get_Info("info:type", 0);

			Entity *e;


			switch( type )
			{
			case ET_CREEP:
				
				e = new Creep();
				break;

			case ET_TOWER:
				
				e = new Tower();
				break;

			case ET_PROJECTILE:
				e = new Projectile();
				break;

			default:
				e = new Entity();
				
			//	cout << "Invalid type " << type << endl;
			//	return false;
				break;
			}//EoS

			//and Configurations
			if(! e->Get_Config( cfg_location )) 
			{
				cout << "Entity Type: " << cfg_location << " failed to load.\n";
				return false;
			}



				//Load Driver Script id function
			e->driverFunctionID = aScriptEngine.GetModule()->GetFunctionIdByName(e->Get_Name().c_str());
			if(e->driverFunctionID < 0)
			{
					e->hasDriver = false;
			}else{
					e->hasDriver = true;
			}
			cout << e->hasDriver << endl;
			cout << "********* " << e->Get_Name() << " has ID # " << e->driverFunctionID << " **************\n\n";

			//add to type vector
		//	e.SetImage(*e.img);
			cout << e->Get_Name() << endl;
			//Type[e.Get_Name()] = e;
			pair<string, Entity *> type_ref = make_pair( e->Get_Name(), e);
			Type.insert(type_ref);
	
	return true;
}//EoF




			
//Update Entity Mechanics
//*****************************************
void Entity_Factory::Update_Logic( float dt, asIScriptContext * context )
{	
	//loop through all entities
	
	list< Entity *>::iterator i = entity_list.begin();

	while(i != entity_list.end())
	{
		if((*i)->Get_State() != ES_NONE)
		{
			//Run Script Driverz
			(*i)->Update_Logic( context );

			i++;
		}else{
			RemoveEntity(i);
		}

	}//end ov While
//	cout << "Entity Size: " << entity_list.size() << "\n";
}//EoF


//Remove entity from list, and delete it from memory
void Entity_Factory::RemoveEntity(list< Entity *>::iterator &i)
{
	(*i)->Destroy();
	safeDelete(*i);
	i = entity_list.erase(i);
	//cout << "is called????" << endl;
}//EoF






//Draw to screen
void Entity_Factory::Draw(sf::RenderWindow *rendow)
{
//	cout << entity_list.size() << endl;

	//loop through all entities
	list< Entity *>::iterator i;

	for(i = entity_list.begin(); i != entity_list.end(); i++)
	{
		(*i)->Render(rendow);
	}//end ov while

//	App::getInstance().Get_RenderWindow()->Draw( circle_target );
}//EoF



void Entity_Factory::Destroy()
{
	//loop through all entities
	map< string, Entity *>::iterator j;
//Clear Type Container
	for(j = Type.begin(); j != Type.end(); j++)
	{
		safeDelete(j->second);
	}
//Clear entity list containter
	list< Entity *>::iterator i;
	for(i = entity_list.begin(); i != entity_list.end(); i++)
	{
		//physicsManager.world->DestroyBody( (*i)->body );
		//safeDelete(*i);
		RemoveEntity(i);
	}

}//EoF